package com.supermap.wzhy.module.fr.service;

import com.supermap.wzhy.data.ConfigHelper;
import com.supermap.wzhy.data.SysConstant;
import com.supermap.wzhy.entity.*;
import com.supermap.wzhy.module.data.dao.MicroIdenmetaDao;
import com.supermap.wzhy.module.data.dao.MicroTablemetaDao;
import com.supermap.wzhy.module.fr.dao.FrDao;
import com.supermap.wzhy.module.fr.dao.WjgDao;
import com.supermap.wzhy.module.user.dao.UserTaskDao;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 年报导出线程
 * Created by sun'f on 17-07-18.
 */
public class FrWFSXExpRunnable  implements  Runnable{
    //年报基本表
    private final  static String BASE_TABLE ="YZYM_E_LI_ILLDISHONESTY";
    //年报标识
    private final  static String BASE_ID ="BACKID";
    //时间戳
    private final  static String TIMESTAMP ="S_EXT_TIMESTAMP";

    /**
     * 年报信息                  YZYM_AN_BASEINFO        <Record></Record>
     * 年报股东出资信息(认缴)     YZYM_AN_SUBCAPITAL      <SUBSCRIBEDINFOS><SUBSCRIBEDINFO></SUBSCRIBEDINFO></SUBSCRIBEDINFOS>
     * 年报股东出资信息(实缴)     YZYM_AN_SUBCAPITAL      <PAIDINFOS><PAIDINFOS></PAIDINFOS></PAIDINFOS>
     * 企业对外投资信息          YZYM_AN_FORINVESTMENT   <INVESINFOS><INVESINFO></INVESINFO></INVESINFOS>
     * 企业年报网站或网店信息     YZYM_AN_WEBSITENINFO    <WEBSITEINFOS><WEBSITEINFO></WEBSITEINFO></WEBSITEINFOS>
     * 企业年报股权变更信息       YZYM_AN_ALTERSTOCKINFO  <EQUITYCHANGEINFOS><EQUITYCHANGEINFO></EQUITYCHANGEINFO></EQUITYCHANGEINFOS>
     *
     * 列入经营异常名录企业信息    YZYM_AO_OPANOMALY
     * 列入严重违法失信企业名单企业信息     YZYM_E_LI_ILLDISHONESTY
     *
     */

    //表
    private final  static String[] SUB_TABLES =new String[]{"YZYM_E_LI_ILLDISHONESTY"};

    //节点
    private final  static String[] SUB_TABLES_NODES =new String[]{"RECORD"};

    FrDao frDao ;
    MicroTablemetaDao microTablemetaDao ;
    MicroIdenmetaDao microIdenmetaDao ;
    UserTaskDao userTaskDao ;

    WjgDao wjgDao;

    Date startTime ;
    Date endTime ;
    int maxSize = 0 ;
    String userName;

    private static  boolean isRunning = false ;

    /**
     * 设置dao
     * @param frDao
     * @param microTablemetaDao
     * @param microIdenmetaDao
     * @param userTaskDao
     */
    public void setDao(FrDao frDao,MicroTablemetaDao microTablemetaDao ,
                       MicroIdenmetaDao microIdenmetaDao ,
                       UserTaskDao userTaskDao,WjgDao wjgDao){
        this.frDao = frDao ;
        this.microIdenmetaDao = microIdenmetaDao ;
        this.microTablemetaDao = microTablemetaDao ;
        this.userTaskDao = userTaskDao ;
        this.wjgDao = wjgDao;
    }

    /**
     * 设置用户名
     * @param request
     * @return
     */
    public String setUserName(HttpServletRequest request){
        if(request != null){
            TUsers users = (TUsers)request.getSession().getAttribute(SysConstant.CURRENT_USER);
            if(users != null){
                this.userName = users.getUserName();
            }else{
                this.userName = "admin";
            }
        }else {
            this.userName = "定时任务";
        }
        return this.userName;
    }


    public  void setMaxSize(int maxSize){
        this.maxSize = maxSize ;
    }

    /**
     * 设置提取数据时间
     * @param startTime
     * @param endTime
     */
    public void setTime(Date startTime,Date endTime){
        this.startTime = startTime ;
        this.endTime = endTime ;
    }

    public boolean checkIsRun(){
        return isRunning ;
    }

    @Override
    public void run() {
        isRunning = true ;
        start();
        isRunning = false ;
    }


    /**
     * 检查是否有文件柜导出任务
     * @return
     */
    private  boolean checkWjgTaskRun(){
        return  userTaskDao.findRunningTask("nb").size()>0; //后面再讲wjg写成常量

    } ;
    /**
     * 生成文件柜数据入口
     */
    public void start(){
        // 记录条数
        int ao = 0;
        //添加任务
        TUsertask usertask = null ;
        int xml_num = 0;
        try {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            SimpleDateFormat sdft = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

            int wjgSum = Integer.valueOf(ConfigHelper.get("wjgsum"));
            System.out.println("文件柜包数 "+wjgSum);

            Object [] params = new String[2];
            int i = 0 ;
            String startTimeStr = startTime==null? "2000-01-01 00:00:00":formatter.format(startTime);
            String endTimeStr = formatter.format(endTime==null?new Date():endTime);

            startTime = startTime==null?sdft.parse("2000-01-01 00:00:00"):startTime;
            endTime = endTime==null?new Date():endTime;

            params[0] = startTimeStr ;
            params[1] = endTimeStr ;

            String sql = " where  to_char(" + TIMESTAMP + ",'%Y-%m-%d') >= ? " +
                    "and  to_char(" + TIMESTAMP + ",'%Y-%m-%d') <= ? ";

            System.out.println("params 1 : "+params[0]+" params 2 : "+params[1]);

            //找到单位记录总数
            Integer count = frDao.getIntegerUniq("select count(*) from " + BASE_TABLE + sql,params) ;

            System.out.println("select count(*) from " + BASE_TABLE + sql);

            if(count == 0){
                return;
            }
            count = maxSize>0&&maxSize<count?maxSize:count ;

            //添加任务
            usertask = addTask(count,"nb");
            System.out.println("年报导出总数: "+count);

            int perSize = 1000 ; //每次取出1千条
            perSize = count<perSize?count:perSize ;
            int num = count/perSize+(count%perSize==0?0:1);
            Map<String ,List<TMicroIdenmeta>> idenMap = new HashMap<>() ;//指标元数据
            //读取全部报表元数据 第一个为基本信息表
            for(String t:SUB_TABLES){
                List<TMicroTablemeta>  tablemetaList  = microTablemetaDao.findByTableName(t);
                if(tablemetaList.size() ==0){
                    continue;
                }
                TMicroTablemeta tablemeta  = tablemetaList.get(0) ;
                idenMap.put(t,microIdenmetaDao.findByWJGIden(tablemeta.getMitmid())) ;

            }
            int xmlNum = 1 ;  //xml数量

            //取一定数目数据
            for(i =1 ; i< num+1 ;i++ ){
                int perCount =0;//每个xml的记录数
                int start = (i-1)*perSize ;
                int end = i*perSize ;
                boolean sub_child_boo = false;

                List<Object[]> dataM  = frDao.query("select skip " + start + " first "
                        + perSize + " cast("+BASE_ID+" as varchar(50))  ID,"
                        + getQueryFields(idenMap.get(BASE_TABLE)) +
                        ",cast("+TIMESTAMP+" as varchar(50))  "+TIMESTAMP+" from "
                        + BASE_TABLE + sql + "order by "+TIMESTAMP+"" , params) ;

                Element data = DocumentHelper.createElement("Data");
                boolean  needExp = false ;
                if(dataM==null){  continue; }  //空值判断
                //一次性获取全部报表数据
                Map<String ,Map<String,List<Object[]>>>  subTableDataMap = new HashMap<>() ;

                //假设每次最多只取1000条
                StringBuilder b = new StringBuilder();
                for(int d= 0; d<dataM.size() ;d++){
                    String id =  dataM.get(d)[0].toString() ;//单位唯一标识
                    b.append("'"+id+"'") ;
                    if(d<dataM.size()-1){
                        b.append(",") ;
                    }
                }
                if(dataM.size()>0){
                    //查询子报表数据  关联查询太慢
                    for(int j=1 ; j< SUB_TABLES.length;j++){
                        if(SUB_TABLES[j]==null||SUB_TABLES[j].equals("")){ continue; }
                        //Object[] params1 =new Object[]{params[0],params[1]} ;
                        //跟企业基础信息表关联

                        String subSql = "select  cast(" + BASE_ID + " as varchar(50))  id,"
                                + getQueryFields(idenMap.get(SUB_TABLES[j])) +
                                " from "+SUB_TABLES[j] + " t   " + sql
                                + " and " + BASE_ID + " in(" + b.toString() + ")";

                        //System.out.println(subSql);
                        List<Object[]> dataList = frDao.query(subSql, params);

                        if (dataList==null) {
                            System.out.println("错误sql:" + subSql );
                            continue;
                        }
                        System.out.println(SUB_TABLES[j]+" 表 dataList size is "+dataList.size());

                        subTableDataMap.put(SUB_TABLES[j],listToMapByEtpsid(dataList)) ;

                    }
                }

                //逐条遍历数据
                for(int d= 0; d<dataM.size() ;d++){
                    sub_child_boo = false;
                    int record_index = (i-1)*perSize+(d+1);

                    //每个xml包record节点从1开始计数,循环
                    if(record_index > wjgSum){
                        record_index = record_index - (int)Math.ceil(record_index/wjgSum)*wjgSum;
                        if(record_index == 0){
                            record_index = wjgSum;
                        }
                    }
                    //System.out.println(record_index);
                    Element record = createRecord(record_index,dataM.get(d),idenMap.get(BASE_TABLE));

                    String id =  dataM.get(d)[0].toString() ;//单位唯一标识

                    //遍历报表取出子节点数据
                    perCount++ ;
                    needExp = true ;

                    for(int j=1 ; j< SUB_TABLES.length;j++){
                        if(SUB_TABLES[j]==null||SUB_TABLES[j].equals("")){ continue; }
                        if(!subTableDataMap.containsKey(SUB_TABLES[j])){ continue;}
                        List<Object[]> dataList =  subTableDataMap.get(SUB_TABLES[j]).get(id);

                        if(dataList == null||dataList.size() ==0){ continue; }

                        Element subTNode = createSubNode(SUB_TABLES_NODES[j],dataList,idenMap.get(SUB_TABLES[j])) ;
                        record.add(subTNode);
                    }

                    ao++;
                    data.add(record);

                    //xmlNum = getXmlNum(xmlNum);//每天零点置零

                    //每wjgSum条输出文件
                    if((d+1)%wjgSum == 0){
                        expOneXmlFile(xmlNum++,perCount,data,endTimeStr,sub_child_boo);
                        needExp =false ;
                        perCount =0 ;
                        data = null ;
                        data = DocumentHelper.createElement("Data");
                        //更新进度
                        updateTask((i-1)*perSize+(d+1),usertask) ;
                    }
                }
                System.out.println("当前已导出 " + ao + " 数据!");

                //剩余部分再导出
                if(needExp){
                    //System.out.println("当前已导出 "+ao+" 数据!");
                    expOneXmlFile(xmlNum++,perCount,data,endTimeStr,sub_child_boo);
                    xml_num = xmlNum;
                    needExp =false ;
                    perCount = 0;
                    //更新进度
                    updateTask(i*perSize,usertask) ;
                }
            }
            //更新进度

            usertask.setFinished(1);
            usertask.setEndtime(new Date());
            updateTask(count, usertask) ;

            addLog(userName,count,startTime,endTime);
            System.out.println("添加文件柜日志成功");
        }   catch (Exception e){
            //异常时删除正在执行的任务
            if(usertask !=null){
                userTaskDao.delete(usertask);
            }
            e.printStackTrace();
        }finally {
            System.out.println("输出企业数据条数 : "+ao);

        }
    }

    //零点重置
    private int getXmlNum(int xmlNum){
        Calendar calendar = Calendar.getInstance();
        int hh = calendar.get(Calendar.HOUR_OF_DAY);
        int min = calendar.get(Calendar.MINUTE);
        //零点重置文件名
        if(hh == 0 && min == 0){
            xmlNum = 1;
        }
        return xmlNum;
    }



    /**
     * 节点文本乱码转换
     * @param txt
     * @return
     */
    private String speacial(String txt){
        String res = "";
        for(int i = 0; i < txt.length(); ++i){
            char ch = txt.charAt(i);
            if( Character.isDefined(ch) && ch!= '&' && ch != '<' && ch != '>'
                    //转换字符是否需要符合高低代理项代码单元?
                    && !Character.isHighSurrogate(ch) && !Character.isISOControl(ch) &&
                    !Character.isLowSurrogate(ch)
                    ){
                res = res + ch;
            }else{
                ch = ' ';
                res += ch;
            }
        }
        return res;
    }


    /**
     * 创建单位数据基本信息节点
     * @param index
     * @param data
     * @param idenmetas
     * @return
     */
    private  Element createRecord(int index ,Object[] data,List<TMicroIdenmeta> idenmetas){
        Element record = DocumentHelper.createElement("Record") ;
        record.addAttribute("index",(index)+"") ;
        int len = idenmetas.size();
        for(int i =0 ; i< len ;i++){
            int type = idenmetas.get(i).getIdenType() ;
            String value = "";
            //格式化日期类型
            if(type == 3){
                try {
                    //yyyy-MM-dd HH:mm:ss
                    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    if(data[i+1] != null){
                        String de = data[i+1].toString();
                        if(de.contains(".") &&de.contains(":") && de.contains("-")&& de.length() > 10){
                            de = de.substring(0, de.indexOf("."));
                        }else if(de.contains(":") && de.contains("-")){
                            value = formatter.format(formatter.parse(de)) ;
                        }else{
                            value = de;
                        }
                    }else {
                        value = "";
                    }

                }   catch (Exception e) {
                    System.out.println("日期类型转换出错："+data[i+1]);
                    value ="";
                }
            }else{
                value = data[i+1]!=null?data[i+1].toString():"" ;

                value = speacial(value);

            }
            // 判断文件柜字段是否有别名
            if(idenmetas.get(i).getWjgName() == null){
                record.addElement(idenmetas.get(i).getIdenCode().toUpperCase()).addText(value) ;
            }else{
                record.addElement(idenmetas.get(i).getWjgName().toUpperCase()).addText(value) ;
            }

        }

        return  record ;
    }

    /**
     * 导出xml文件
     * @param data
     * @return
     */
    private boolean expOneXmlFile(int xmlNum ,int perCount,Element data,String date,boolean coo){

        String number = String.format("%06d", xmlNum);

        String endTime = date.replaceAll("-","");

        String dataPackNum =  "130000" + endTime + number ;   //数据包编号
        //根节点
        Element root = DocumentHelper.createElement("Package");
        //System.out.println("导出 " +dataPackNum);
        Element head = createHead(dataPackNum,perCount) ;//数据头部描述信息
        root.add(head);
        root.add(data);
        //输出文件
        createXmlFile(xmlNum,root,endTime,coo);
        return true ;
    }
    /**
     * 获取子报表的节点
     * @param nodeName
     * @param dataList
     * @param idenmetas
     * @return
     */
    private  Element createSubNode(String nodeName ,List<Object[]>dataList,List<TMicroIdenmeta> idenmetas){
        String[] nodes = nodeName.split("-") ;//多个则要考虑多个子节点
        Element rootNode = DocumentHelper.createElement(nodes[0]) ;
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat formatter2 = new SimpleDateFormat("yyyy-MM-dd");
        int len = idenmetas.size();
        for(Object[] data:dataList){
            Element subNode = nodes.length>1?DocumentHelper.createElement(nodes[1]):null ;
            for(int i =0 ; i< len ;i++){
                int type = idenmetas.get(i).getIdenType() ;
                //int dsize = dataList.size() ;
                String value = "";
                //格式化日期类型
                if(type == 3){
                    try {
                        if(data[i+1] != null){
                            String de = data[i+1].toString();
                            if(de.contains(".") && de.length() > 10 && de.contains("-") && de.contains(":")){
                                de = de.substring(0, de.indexOf("."));
                                value = formatter.format(formatter.parse(de)) ;
                            }else if (de.contains("-") && de.contains(":")){
                                value = formatter2.format(formatter2.parse(de)) ;
                            }else {
                                value = de;
                            }
                        }else {
                            value = "";
                        }

                        // value = formatter.format(data[i+1]) ;
                    }   catch (Exception e) {
//                   System.out.println("日期类型转换出错："+data[i+1]);
                        value ="";
                    }
                }else{
                    value = data[i+1]!=null?data[i+1].toString():"" ;

                    value = speacial(value);
                }
                if(subNode == null){
                    if(idenmetas.get(i).getWjgName() != null){
                        rootNode.addElement(idenmetas.get(i).getWjgName().toUpperCase()).addText(value) ;
                    }else{
                        rootNode.addElement(idenmetas.get(i).getIdenCode().toUpperCase()).addText(value) ;
                    }
                }else{
                    if(idenmetas.get(i).getWjgName() != null){
                        subNode.addElement(idenmetas.get(i).getWjgName().toUpperCase()).addText(value) ;
                    }else{
                        subNode.addElement(idenmetas.get(i).getIdenCode().toUpperCase()).addText(value) ;
                    }
                }
            }
            if(subNode != null){
                rootNode.add(subNode) ;
            }
        }
        return  rootNode ;
    }

    /**
     * 输出xml文件到指定目录
     * @param root
     * @return
     */
    private  boolean createXmlFile(int xmlnum , Element root,String date,boolean coo){
        //设置文件编码
        OutputFormat xmlFormat = new OutputFormat();
        xmlFormat.setEncoding("UTF-8");

        // 设置换行
        xmlFormat.setNewlines(true);
        // 生成缩进
        xmlFormat.setIndent(true);
        xmlFormat.setNewLineAfterDeclaration(false);
        // 使用4个空格进行缩进, 可以兼容文本编辑器
        xmlFormat.setIndent("    ");
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
//        第1到6位为省级行政区划代码；
//        第7到14位为数据交换的日期，表示方法为：YYYYMMDD；
//        第15到20位为顺序号，每天零点置零，以000001为始。
//        举例：WZHY_GSXX_WFSX_13000020161025000001.xml
        String number = String.format("%06d", xmlnum);
        //文件名包含的日期不能含有'-'
        //date = date.replaceAll("-", "");
        String base =  ConfigHelper.get("wjgpath")+"/"+date +"/"+"tjdata"+"/"+"WFSX"+"/";
        String fileName = "WZHY_GSXX_WFSX_130000"+ date +number +".xml";
        if(coo){
            System.out.println(fileName);
        }

        String path = base+fileName ;
        File f = new File(base) ;
        if(!f.exists()){ f.mkdirs() ;}
        if(new File(path).exists()){ new File(path).delete() ; }
        try {
            Document document = DocumentHelper.createDocument();
            document.add(root);
            document.setXMLEncoding("UTF-8");

            //创建写文件方法
            //FileOutputStream fos = new FileOutputStream(path);
            XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(path),xmlFormat);

            //写入文件
            xmlWriter.write(document);
            //关闭
            xmlWriter.close();
            //System.out.println();
            return  true ;
        }   catch (Exception e){
            e.printStackTrace();
        }
        return  false ;

    }

    /**
     * 构造表头
     * @return
     */
    private Element createHead(String dataPackNum,int perCount){
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddhhmmss");
        Element root = DocumentHelper.createElement("PackageHead") ;
        root.addElement("SJBBH").addText(dataPackNum);   // 数据包编号
        root.addElement("SJBLX").addText("GSXX") ;  //数据包类型
        root.addElement("DWDM").addText("130000") ;    //单位代码
        root.addElement("DWMC").addText("河北省工商行政管理局") ;    //单位名称
        root.addElement("JLS").addText(perCount+"") ;     //记录数
        //数据包生成时间，格式为yyyyMMddhhmmss
        root.addElement("INFODATE").addText(formatter.format(new Date())) ;
        root.addElement("SJLX").addText("WFSX");
        return  root ;
    }

    /**
     * 获取查询指标字符串
     * @param idenmetas
     * @return
     */
    private String  getQueryFields(List<TMicroIdenmeta> idenmetas){
        StringBuilder b = new StringBuilder() ;
        int index =0 ;
        int size = idenmetas.size() ;
        for(TMicroIdenmeta i:idenmetas){
            //informix char类型转换
            if(i.getIdenType() == 1&&i.getIdenLength()<=255){
                b.append("cast("+i.getIdenCode().toUpperCase()+ " as varchar(255)) "+i.getIdenCode().toUpperCase());
            }else if(i.getIdenType() == 1&&i.getIdenLength()==3000){
                //lvarchar : informix独有的长字符类型
                b.append("cast("+i.getIdenCode().toUpperCase()+ " as lvarchar(3000)) "+i.getIdenCode().toUpperCase());
            }
            else{
                b.append(i.getIdenCode().toUpperCase());
            }

            if(index <size-1){
                b.append(",");
            }
            index++ ;
        }
        return b.toString() ;
    }


    /**
     * 将datalist转换成map对象
     * @param datalist
     * @return
     */
    private Map<String , List<Object[]>>  listToMapByEtpsid(List<Object[]> datalist){

        Map<String , List<Object[]>> map = new HashMap<>() ;
        for(Object[] o:datalist){
            String id =  o[0].toString() ;//单位唯一标识
            if(!map.containsKey(id)){
                map.put(id,new ArrayList<Object[]>());
            }
            map.get(id).add(o);
        }
        return  map ;
    }


    /**
     * 添加文件柜导出任务
     * @param count
     * @param type
     * @return
     */
    private TUsertask  addTask(int count,String type){
        TUsertask t = new TUsertask() ;
        t.setTaskType(type);
        t.setStarttime(new Date());
        t.setCount(String.valueOf(count));
        t.setFinished(0); //未完成标识
        return  userTaskDao.save(t) ;
    };


    private TUsertask  updateTask(int completed,TUsertask task){
        task.setCompleted(String.valueOf(completed));
        return  userTaskDao.save(task) ;
    };


    public boolean addLog(String username,int record,Date starttime,Date endtime){

        TUserlogFrwjg log = new TUserlogFrwjg();

        log.setUsername(username);
        log.setUnitrecord(record);
        log.setStarttime(starttime);
        log.setEndtime(endtime);
        try {
            wjgDao.save(log);
        }catch (Exception e){
            System.out.println("保存文件柜日志异常");
        }
        return true;
    }
}
